package com.ld.common.exception;

import java.lang.reflect.Field;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.validator.internal.engine.ConstraintViolationImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;

import com.ld.shieldsb.annotation.util.AnnotationUtil;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.model.ResultCode;
import com.ld.shieldsb.common.core.reflect.ModelUtil;
import com.ld.shieldsb.common.web.exception.ParamsterValidException;
import com.ld.shieldsb.common.web.util.Web;
import com.ld.shieldsb.dao.exception.SensitiveWordContainsException;

import lombok.extern.slf4j.Slf4j;

@ConditionalOnProperty(prefix = "modules.sc.exceptionhandler", name = "enable", havingValue = "true", matchIfMissing = true)
@ControllerAdvice
@Slf4j
public class GlobalDefaultExceptionHandler {
    public static final String DEFAULT_ERROR_VIEW = "error/error";

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result handleIllegalParamException(MethodArgumentNotValidException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String tips = "参数不合法";
        if (errors.size() > 0) {
            tips = errors.get(0).getDefaultMessage();
        }
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 计算错误
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public Result handleArithmeticException(ArithmeticException e) {
        String tips = "计算错误：" + e.getMessage();
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 方法不支持
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        String tips = "方法不支持：" + e.getMessage();
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 文件大小超过限制
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseBody
    public Result maxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        String tips = "文件大小超出限制：" + e.getMessage();
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 文件大小超过限制
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public Result nullPointerException(NullPointerException e) {
        String tips = "数据为空：" + e.getMessage();
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 返回信息写入异常
    @ExceptionHandler(HttpMessageNotWritableException.class)
    @ResponseBody
    public Result httpMessageNotWritableException(HttpMessageNotWritableException e) {
        String tips = "内容转换出错：" + e.getMessage();
        e.printStackTrace();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 实体类数据保存存在敏感词
    @ExceptionHandler(SensitiveWordContainsException.class)
    @ResponseBody
    public Result sensitiveWordContainsException(SensitiveWordContainsException e) {
        String tips = e.getMessage();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 参数异常(搜索时解密或者验证是否属于查询类的)
    @ExceptionHandler(ParamsterValidException.class)
    @ResponseBody
    public Result paramsterValidException(ParamsterValidException e) {
        String tips = "参数异常：" + e.getMessage();
        Result result = new Result(false, ResultCode.ERROR_PARAM_NOT_VALID, tips);
        return result;
    }

    // 统一处理表单验证的异常
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result validateErrorHandler(HttpServletRequest request, HttpServletResponse response, BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<FieldError> errorList = bindingResult.getFieldErrors();
            String errorMsg = "检测到" + errorList.size() + "处错误:";
            for (int i = 0; i < errorList.size(); i++) {
                FieldError error = errorList.get(i);
                String fieldShowName = error.getField();
                Object sourceObj = ModelUtil.getModelValue(error, "source");
                if (sourceObj instanceof ConstraintViolationImpl) { // 获取类信息
                    ConstraintViolationImpl<?> source = (ConstraintViolationImpl<?>) sourceObj;
                    Class<?> clazz = source.getRootBeanClass();
                    try {
                        Field field = clazz.getDeclaredField(error.getField());
                        fieldShowName = AnnotationUtil.getFiledShowName(field, fieldShowName);
                    } catch (NoSuchFieldException | SecurityException e1) {
                        log.error("", e);
                    }
                }
                String msg = fieldShowName + error.getDefaultMessage();
                if (msg.contains("Failed to convert property")) {
                    log.warn("字段转换错误：" + msg);
                    msg = fieldShowName + "字段转换错误！";
                } else { // 字段验证错误

                }
                errorMsg += "\n";
                if (errorList.size() > 1) {
                    errorMsg += (i + 1) + "、";
                }
                errorMsg += msg;
            }
//            String errorMsg = errorList.get(0).getField() + " 字段错误，错误原因:" + errorList.get(0).getDefaultMessage();
            return Web.Response.error(errorMsg);
//            return Web.Response.error(bindingResult.getFieldError().getDefaultMessage());
        } else {
            return Web.Response.error("未知错误！");
        }
    }

    // 这个注解是指当指定处理的异常类
    @ExceptionHandler()
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) {
//      // If the exception is annotated with @ResponseStatus rethrow it and let
//      // the framework handle it - like the OrderNotFoundException example
//      // at the start of this post.
//      // AnnotationUtils is a Spring Framework utility class.
//      if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
//          throw e;
//
//      // Otherwise setup and send the user to a default error-view.

        /*
        * 返回json数据或者String数据：
        * 那么需要在方法上加上注解：@ResponseBody
        * 添加return即可。
        */
        /*
        * 返回视图：
        * 定义一个ModelAndView即可，
        * 然后return;
        * 定义视图文件(比如：error.html,error.ftl,error.jsp);
        *
        */
        // 打印异常信息：
        log.error("异常捕获：" + req.getRequestURI(), e);
        ModelAndView mav = new ModelAndView();
        mav.addObject("message", e.getMessage());
        mav.addObject("path", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

}